﻿using Ivi.Visa;
using log4net;
using NationalInstruments.Visa;
using TCommon;
using TCommon.Extension;
using TCommunication.Impl;

namespace TCommunication.FormatIo;

public class Rs232FormatIo
{
    /// <summary>
    /// 读取byte缓存
    /// </summary>
    private byte[]? _byteCache;
    /// <summary>
    /// 读取string缓存
    /// </summary>
    private string? _strCache;
    /// <summary>
    /// 读取byte时防止线程重入
    /// </summary>
    private object _readByteLockObject = new object();
    /// <summary>
    /// 读取String时防止线程重入
    /// </summary>
    private object _readStringLockObject = new object();
    /// <summary>
    /// 触发接收事件后延迟读取的时间
    /// </summary>
    private int _readDelay = 10;
    /// <summary>
    /// 用于写入后等待接收
    /// </summary>
    private ManualResetEventSlim? _resetEvent;

    private ILog _log;
    /// <summary>
    /// 构造
    /// </summary>
    /// <param name="communication"></param>
    public Rs232FormatIo(VisaSerialCommunication communication)
    {
        Communication = communication;
        this._log = LogManager.GetLogger(GetType());
    }

    #region 属性

    /// <summary>
    /// 通讯接口
    /// </summary>
    public VisaSerialCommunication Communication { get; }

    #endregion

    #region 方法

    #region 公开方法

    /// <summary>
    /// 发送byte[]并读取下一条回复
    /// </summary>
    /// <param name="cmd">发送的指令</param>
    /// <param name="timeOut">超时时间</param>
    /// <param name="readDelay">触发接收事件后的读取延时</param>
    /// <returns></returns>
    public OperateResult<byte[]> SendAndReceived(byte[] cmd,int timeOut = 5000,int readDelay = 10)
    {
        if (Communication.Session == null)
        {
            return OperateResult.CreateFailedResult<byte[]>("请先进行连接");
        }
        else
        {
            if (Communication.Session is SerialSession serialSession)
            {
                try
                {
                    serialSession.LockResource();
                    serialSession.Clear();
                    _resetEvent = new ManualResetEventSlim(false);
                    _readDelay = readDelay;
                    serialSession.AnyCharacterReceived += OnAnyCharacterReceivedForByte;
                    var operateResult = Write(cmd);
                    if (operateResult.IsFailed)
                    {
                        return OperateResult.CreateFailedResult<byte[]>(operateResult.Message);
                    }
                    var wait = _resetEvent.Wait(TimeSpan.FromMilliseconds(timeOut));
                    if (wait)
                    {
                        if (_byteCache != null)
                        {
                            return OperateResult.CreateSuccessResult(_byteCache);
                        }
                        else
                        {
                            return OperateResult.CreateFailedResult<byte[]>("读取错误:未读取到任何byte");
                        }
                    }
                    else
                    {
                        return OperateResult.CreateFailedResult<byte[]>("读取超时");
                    }
                }
                catch (Exception e)
                {
                    return OperateResult.CreateFailedResult<byte[]>($"通讯异常:{e}");
                }
                finally
                {
                    if (serialSession.ResourceLockState != ResourceLockState.NoLock)
                    {
                        serialSession.UnlockResource();
                    }
                    _resetEvent?.Dispose();
                    _byteCache = null;
                    _readDelay = 10;
                }
            }
            else
            {
                return OperateResult.CreateFailedResult<byte[]>("非串口连接");
            }
        }
    }
    /// <summary>
    /// 发送string并读取下一条回复
    /// </summary>
    /// <param name="cmd">指令</param>
    /// <param name="timeOut">超时时间</param>
    /// <param name="readDelya">触发接收事件后的读取延时</param>
    /// <returns></returns>
    public OperateResult<string> SendAndReceived(string cmd, int timeOut = 5000,int readDelya = 10)
    {
        if (Communication.Session == null)
        {
            return OperateResult.CreateFailedResult<string>("请先进行连接");
        }
        else
        {
            if (Communication.Session is SerialSession serialSession)
            {
                try
                {
                    serialSession.LockResource();
                    serialSession.Clear();
                    _resetEvent = new ManualResetEventSlim(false);
                    _readDelay = readDelya;
                    serialSession.AnyCharacterReceived += OnAnyCharacterReceivedForString;
                    var sendOnlyResult = Write(cmd);
                    if (sendOnlyResult.IsFailed)
                    {
                        return OperateResult.CreateFailedResult<string>(sendOnlyResult.Message);
                    }
                    var wait = _resetEvent.Wait(TimeSpan.FromMilliseconds(timeOut));
                    if (wait)
                    {
                        if (_strCache != null)
                        {
                            return OperateResult.CreateSuccessResult<string>(_strCache);
                        }
                        else
                        {
                            return OperateResult.CreateFailedResult<string>("读取错误:未读取到任何byte");
                        }
                    }
                    else
                    {
                        return OperateResult.CreateFailedResult<string>("读取超时");
                    }
                }
                catch (Exception e)
                {
                    return OperateResult.CreateFailedResult<string>($"通讯异常:{e}");
                }
                finally
                {
                    serialSession.AnyCharacterReceived -= OnAnyCharacterReceivedForString;
                    if (serialSession.ResourceLockState != ResourceLockState.NoLock)
                    {
                        serialSession.UnlockResource();
                    }
                    _resetEvent?.Dispose();
                    _strCache = null;
                    _readDelay = 10;
                }
            }
            else
            {
                return OperateResult.CreateFailedResult<string>("非串口连接");

            }
        }
    }
    /// <summary>
    /// 写入byte[]
    /// </summary>
    /// <param name="cmd">指令</param>
    /// <returns></returns>
    public OperateResult Write(byte[] cmd)
    {
        if (Communication.Session == null)
        {
            return OperateResult.CreateFailedResult<string>("请先进行连接");
        }
        else
        {
            var rawIo = Communication.Session.RawIO;
            try
            {
                _log.Info($"串口{Communication.PortName}写入\tbyte[]:{cmd.ToHexStrFromBytes()}");
                //formattedIo.WriteBinary(cmd);
                rawIo.Write(cmd);
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception e)
            {
                _log.Error($"串口{Communication.PortName}写入异常:{e}");
                return OperateResult.CreateFailedResult($"写入异常:{e}");
            }
        }
    }

    /// <summary>
    /// 写String
    /// </summary>
    /// <param name="cmd">指令</param>
    /// <returns></returns>
    public OperateResult Write(string cmd)
    {
        if (Communication.Session == null)
        {
            return OperateResult.CreateFailedResult<string>("请先进行连接");
        }
        else
        {
            var rawIo = Communication.Session.RawIO;
            //var formattedIo = Communication.Session.FormattedIO;
            try
            {
                _log.Info($"串口{Communication.PortName}写入\tString:{cmd}");
                //formattedIo.WriteLine(cmd);
                rawIo.Write(cmd);
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception e)
            {
                _log.Error($"串口{Communication.PortName}写入异常:{e}");
                return OperateResult.CreateFailedResult($"写入异常:{e}");
            }
        }
    }

    #endregion

    #region 内部调用

    /// <summary>
    /// 收到数据回调，缓存为byte[]
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnAnyCharacterReceivedForByte(object? sender, VisaEventArgs e)
    {
        if (!Monitor.TryEnter(_readByteLockObject))
        {
            return;
        }
        var serialSession = (sender as SerialSession)!;
        serialSession.AnyCharacterReceived -= OnAnyCharacterReceivedForByte;
        Thread.Sleep(_readDelay);
        var rawIo = serialSession.RawIO;
        try
        {
            var length = serialSession.BytesAvailable;
            this._byteCache = rawIo.Read(length);
            _log.Info($"串口{Communication.PortName}读取\tByte:{_byteCache.ToHexStrFromBytes()}");
        }
        catch (Exception exception)
        {
            _log.Error($"串口{Communication.PortName}读取异常:{exception}");
        }
        finally
        {
            _resetEvent?.Set();
            Monitor.Exit(_readByteLockObject);
        }
    }
    /// <summary>
    /// 收到数据回调，缓存为string
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnAnyCharacterReceivedForString(object? sender, VisaEventArgs e)
    {
        if (!Monitor.TryEnter(_readStringLockObject))
        {
            return;
        }
        var serialSession = (sender as SerialSession)!;
        serialSession.AnyCharacterReceived -= OnAnyCharacterReceivedForString;
        Thread.Sleep(_readDelay);
        try
        {
            var rawIo = serialSession.RawIO;
            var length = serialSession.BytesAvailable;
            this._strCache = rawIo.ReadString(length);
            _log.Info($"串口{Communication.PortName}读取\tString:{_strCache}");
        }
        catch (Exception exception)
        {
            _log.Error($"串口{Communication.PortName}读取异常:{exception}");
        }
        finally
        {
            _resetEvent?.Set();
            Monitor.Exit(_readStringLockObject);
        }
    }

    #endregion

    #endregion
}